home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Netware Super Library
/
Netware Super Library.iso
/
mis_util
/
ask11
/
ask.asm
next >
Wrap
Assembly Source File
|
1991-11-10
|
44KB
|
1,185 lines
;--------------------------------------------------------------------------;
; Program: Ask .Asm ;
; Purpose: Ask question in batch file and time out if desired. ;
; Notes: Compiles under TURBO Assembler, v2.0. Should work on any ;
; machine running MS-DOS, v2.xx or higher. ;
; Status: Released into the public domain. Enjoy! If you use it, ;
; let me know what you think. You don't have to send ;
; any money, just comments and suggestions. ;
; Updates: 23-Apr-90, v1.0, GAT ;
; - initial version. ;
; 13-May-90, GAT ;
; - fixed problem with return code if time-out reached. ;
; 08-Jul-90, GAT ;
; - added macros to push/pop registers. ;
; 28-Aug-90, v1.1a, GAT ;
; - put equates and macros in separate files. ;
; - put common routines in libs. ;
; 12-Oct-91, v1.1b, GAT ;
; - revised include file names. ;
; - replaced references to Push_M and Pop_M macros with ;
; calls to push and pop. ;
; - removed local stack: it's not necessary. ;
;--------------------------------------------------------------------------;
;--------------------------------------------------------------------------;
; Author: George A. Theall ;
; Phone: +1 215 662 0558 ;
; SnailMail: TifaWARE ;
; 506 South 41st St., #3M ;
; Philadelphia, PA. 19104 USA ;
; E-Mail: theall@gdalsrv.sas.upenn.edu (Internet) ;
;--------------------------------------------------------------------------;
;--------------------------------------------------------------------------;
; D I R E C T I V E S ;
;--------------------------------------------------------------------------;
DOSSEG
MODEL tiny
IDEAL
LOCALS
JUMPS
;
; This section comes from Misc.Inc.
;
@16BIT EQU (@CPU AND 8) EQ 0
@32BIT EQU (@CPU AND 8)
MACRO ZERO RegList ;; Zeros registers
IRP Reg, <RegList>
xor Reg, Reg
ENDM
ENDM
;
; This section comes from DOS.Inc.
;
BELL EQU 7
BS EQU 8
TAB EQU 9
CR EQU 13
LF EQU 10
ESCAPE EQU 27 ; nb: ESC is a TASM keyword
SPACE EQU ' '
KEY_F1 EQU 3bh
KEY_F2 EQU 3ch
KEY_F3 EQU 3dh
KEY_F4 EQU 3eh
KEY_F5 EQU 3fh
KEY_F6 EQU 40h
KEY_F7 EQU 41h
KEY_F8 EQU 42h
KEY_F9 EQU 43h
KEY_F10 EQU 44h
KEY_HOME EQU 47h
KEY_UP EQU 48h
KEY_PGUP EQU 49h
KEY_LEFT EQU 4bh
KEY_RIGHT EQU 4dh
KEY_END EQU 4fh
KEY_DOWN EQU 50h
KEY_PGDN EQU 51h
KEY_INS EQU 52h
KEY_DEL EQU 53h
KEY_C_F1 EQU 5eh
KEY_C_F2 EQU 5fh
KEY_C_F3 EQU 60h
KEY_C_F4 EQU 61h
KEY_C_F5 EQU 62h
KEY_C_F6 EQU 63h
KEY_C_F7 EQU 64h
KEY_C_F8 EQU 65h
KEY_C_F9 EQU 66h
KEY_C_F10 EQU 67h
KEY_C_LEFT EQU 73h
KEY_C_RIGHT EQU 74h
KEY_C_END EQU 75h
KEY_C_PGDN EQU 76h
KEY_C_HOME EQU 77h
KEY_C_PGUP EQU 84h
KEY_F11 EQU 85h
KEY_F12 EQU 86h
KEY_C_F11 EQU 89h
KEY_C_F12 EQU 8ah
DOS EQU 21h ; main MSDOS interrupt
STDIN EQU 0 ; standard input
STDOUT EQU 1 ; standard output
STDERR EQU 2 ; error output
STDAUX EQU 3 ; COM port
STDPRN EQU 4 ; printer
TSRMAGIC EQU 424bh ; magic number
STRUC ISR
Entry DW 10EBh ; short jump ahead 16 bytes
OldISR DD ? ; next ISR in chain
Sig DW TSRMAGIC ; magic number
EOIFlag DB ? ; 0 (80) if soft(hard)ware int
Reset DW ? ; short jump to hardware reset
Reserved DB 7 dup (0)
ENDS
STRUC ISRHOOK
Vector DB ? ; vector hooked
Entry DW ? ; offset of TSR entry point
ENDS
STRUC TSRSIG
Company DB 8 dup (" ") ; blank-padded company name
Product DB 8 dup (" ") ; blank-padded product name
Desc DB 64 dup (0) ; ASCIIZ product description
ENDS
GLOBAL at : PROC
GLOBAL errmsg : PROC
GLOBAL ProgName : BYTE ; needed for errmsg()
GLOBAL EOL : BYTE ; ditto
GLOBAL fgetc : PROC
GLOBAL fputc : PROC
GLOBAL fputs : PROC
GLOBAL getchar : PROC
GLOBAL getdate : PROC
GLOBAL getswtch : PROC
GLOBAL gettime : PROC
GLOBAL getvdos : PROC
GLOBAL getvect : PROC
GLOBAL isatty : PROC
GLOBAL kbhit : PROC
GLOBAL pause : PROC
GLOBAL putchar : PROC
GLOBAL setvect : PROC
GLOBAL sleep : PROC
GLOBAL find_NextISR : PROC
GLOBAL find_PrevISR : PROC
GLOBAL hook_ISR : PROC
GLOBAL unhook_ISR : PROC
GLOBAL free_Env : PROC
GLOBAL fake_Env : PROC
GLOBAL check_ifInstalled : PROC
GLOBAL install_TSR : PROC
GLOBAL remove_TSR : PROC
;
; This section comes from Math.Inc.
;
GLOBAL atoi : PROC
GLOBAL atou : PROC
GLOBAL utoa : PROC
;
; This section comes from String.Inc.
;
EOS EQU 0 ; terminates strings
GLOBAL isdigit : PROC
GLOBAL islower : PROC
GLOBAL isupper : PROC
GLOBAL iswhite : PROC
GLOBAL memcmp : PROC
GLOBAL strchr : PROC
GLOBAL strcmp : PROC
GLOBAL strlen : PROC
GLOBAL tolower : PROC
GLOBAL toupper : PROC
VERSION equ '1.1b' ; current version of program
ERRH equ 255 ; errorlevel if help given
;--------------------------------------------------------------------------;
; C O D E S E G M E N T ;
;--------------------------------------------------------------------------;
CODESEG
ORG 80h ; commandline
LABEL CmdLen BYTE
db ?
LABEL CmdLine BYTE
db 127 dup (?)
ORG 100h ; start of .COM file
STARTUPCODE
jmp main ; skip over data and stack
;--------------------------------------------------------------------------;
; D A T A ;
;--------------------------------------------------------------------------;
LABEL ProgName BYTE
db 'ask: ', EOS
LABEL EOL BYTE
db '.', CR, LF, EOS
LABEL HelpMsg BYTE
db CR, LF
db 'TifaWARE ASK, v', VERSION, ', ', ??Date
db ' - ask questions in batch files.', CR, LF
db 'Usage: ask [-options] [msgtxt]', CR, LF, LF
db 'Options:', CR, LF
db ' -l = convert response to lower case', CR, LF
db ' -tn = wait n seconds before timing out', CR, LF
db ' -u = convert response to upper case', CR, LF
db ' -? = display this help message', CR, LF, LF
db 'msgtxt is an optional message to display.', CR, LF, EOS
LABEL Err1Msg BYTE
db 'illegal option -- '
LABEL OptCh BYTE
db ?
db EOS
LABEL Err2Msg BYTE
db 'time-out value not specified', EOS
LABEL Err3Msg BYTE
db 'time-out value too large -- ', EOS
SwitCh db '-' ; char introducing options
HFlag db 0 ; flag for on-line help
LFlag db 0 ; flag for lowercase response
TFlag db 0 ; flag for time-out
UFlag db 0 ; flag for uppercase response
Delay dw ? ; time to pause for key
MsgLen db 0 ; length of message text
MsgTxt dw ? ; near pointer to message text
RCode db 0 ; program return code
;--------------------------------------------------------------------------;
; P R O C E D U R E S ;
;--------------------------------------------------------------------------;
;---- skip_Spaces -------------------------------------------------------;
; Purpose: Skips past spaces in a string. ;
; Notes: Scanning stops with either a non-space *OR* CX = 0. ;
; Entry: DS:SI = start of string to scan. ;
; Exit: AL = next non-space character, ;
; CX is adjusted as necessary, ;
; DS:SI = pointer to next non-space. ;
; Calls: none ;
; Changes: AL, CX, SI ;
;--------------------------------------------------------------------------;
PROC skip_Spaces
jcxz SHORT @@Fin
@@NextCh:
lodsb
cmp al, ' '
loopz @@NextCh
jz SHORT @@Fin ; CX = 0; don't adjust
inc cx ; adjust counters if cx > 0
dec si
@@Fin:
ret
ENDP skip_Spaces
;---- get_Opt -----------------------------------------------------------;
; Purpose: Get a commandline option. ;
; Notes: none ;
; Entry: AL = option character, ;
; CX = count of characters left in commandline, ;
; DS:SI = pointer to first option to process. ;
; Exit: CX = count of characters left _after_ processing, ;
; DS:SI = pointer to whitespace _after_ options, ;
; Calls: tolower, errmsg, isdigit, atou, fputs ;
; Changes: AX, BL, CX, DX, SI, ;
; [OptCh], [HFlag], [LFlag], [TFlag], [UFlag], [Delay] ;
;--------------------------------------------------------------------------;
PROC get_Opt
mov [OptCh], al ; save for later
call tolower ; use only lowercase in cmp.
cmp al, 'l'
jz SHORT @@OptL
cmp al, 't'
jz SHORT @@OptT
cmp al, 'u'
jz SHORT @@OptU
cmp al, '?'
jz SHORT @@OptH
mov dx, OFFSET Err1Msg ; unrecognized option
call errmsg ; then *** DROP THRU *** to OptH
;
; Various possible options.
;
@@OptH:
mov [HFlag], 1 ; set help flag
jmp SHORT @@Fin
@@OptL:
mov [LFlag], 1 ; set lowercase flag
jmp SHORT @@Fin
@@OptT:
mov [TFlag], 1 ; set time-out flag
mov al, [BYTE PTR si] ; get next character
call isdigit ; if not a digit, trouble!
jz SHORT @@GetDelay
mov dx, OFFSET Err2Msg ; no delay specified
call errmsg
jmp @@OptH
@@GetDelay:
mov dx, si ; save to adjust CX and if error
call atou
pushf ; preserve flags
add cx, dx ; adjust counter
sub cx, si
popf ; restore flags
jc SHORT @@BadDelay ; error in conversion?
cmp ax, 60*60*12 ; 12 or more hours?
jae SHORT @@BadDelay ; yes, bad delay
mov [Delay], ax
jmp SHORT @@Fin
@@BadDelay:
push dx
mov bx, STDERR
mov dx, OFFSET ProgName
call fputs
mov dx, OFFSET Err3Msg
call fputs
pop dx
mov al, [si] ; save next non-digit
mov [BYTE PTR si], EOS ; replace with EOS
call fputs
mov [si], al ; restore it
mov dx, OFFSET EOL
call fputs
jmp SHORT @@OptH
@@OptU:
mov [UFlag], 1 ; set uppercase flag
@@Fin:
ret
ENDP get_Opt
;---- get_Arg -----------------------------------------------------------;
; Purpose: Gets a non-option from the set of commandline arguments. ;
; Notes: Anything left on the commandline is user's message text. ;
; Entry: CX = count of characters left in commandline, ;
; DS:SI = pointer to argument to process. ;
; Exit: CX = zero ;
; DS:SI = points to CR after commandline. ;
; Calls: none ;
; Changes: CX, SI ;
; [MsgLen], [MsgTxt] ;
;--------------------------------------------------------------------------;
PROC get_Arg
mov [MsgLen], cl ; for safekeeping
mov [MsgTxt], si
add si, cx ; adjust so nothing's left
ZERO cl
mov [BYTE PTR si], EOS ; finish off string
ret
ENDP get_Arg
;---- process_CmdLine ---------------------------------------------------;
; Purpose: Processes commandline arguments. ;
; Notes: A switch character by itself is ignored. ;
; No arguments whatsoever causes help flag to be set. ;
; Entry: n/a ;
; Exit: n/a ;
; Calls: skip_Spaces, get_Opt, get_Arg ;
; Changes: AX, CX, SI, ;
; BL, DX (get_Opt), ;
; [HFlag], ;
; [OptCh], [LFlag], [TFlag], [UFlag], [Delay] (get_Opt), ;
; [MsgLen], [MsgTxt] (get_Arg), ;
; Direction flag is cleared. ;
;--------------------------------------------------------------------------;
PROC process_CmdLine
cld ; forward, march!
ZERO ch, ch
mov cl, [CmdLen] ; length of commandline
mov si, OFFSET CmdLine ; offset to start of commandline
call skip_Spaces ; check if any args supplied
or cl, cl
jnz SHORT @@ArgLoop
mov [HFlag], 1 ; if none, set help flag
jmp SHORT @@Fin
;
; For each blank-delineated argument on the commandline...
;
@@ArgLoop:
lodsb ; next character
dec cl
cmp al, [SwitCh] ; is it the switch character?
jnz SHORT @@NonOpt ; no
;
; Isolate each option and process it. Stop when a space is reached.
;
@@OptLoop:
jcxz SHORT @@Fin ; abort if nothing left
lodsb
dec cl
cmp al, ' '
jz SHORT @@NextArg ; abort when space reached
call get_Opt
jmp @@OptLoop
;
; Process the current argument, which is *not* an option.
; Then, *drop thru* to advance to next argument.
;
@@NonOpt:
dec si ; back up one character
inc cl
call get_Arg
;
; Skip over spaces until next argument is reached.
;
@@NextArg:
call skip_Spaces
or cl, cl
jnz @@ArgLoop
@@Fin:
ret
ENDP process_CmdLine
;--------------------------------------------------------------------------;
; E N T R Y P O I N T ;
;--------------------------------------------------------------------------;
;---- main --------------------------------------------------------------;
; Purpose: Main section of program. ;
; Notes: none ;
; Entry: Arguments as desired ;
; Exit: Return code as follows: ;
; 0 => program timed-out ;
; 255 => on-line help requested ;
; else => ASCII value of character pressed. ;
; Calls: process_CmdLine, fputs, pause, getchar, tolower, toupper ;
; Changes: n/a ;
;--------------------------------------------------------------------------;
main:
;
; Process commandline arguments. If the variable HFlag is set, then
; on-line help is displayed and the program immediately terminates.
;
call process_CmdLine ; process commandline args
cmp [HFlag], 0 ; is help needed?
jz SHORT @@NoHelp ; no
mov al, ERRH ; yes, so set return code
mov bx, STDERR
mov dx, OFFSET HelpMsg ; point to help message
call fputs
jmp SHORT @@Fin ; and jump to end of program
;
; Display any message from commandline then get keypress from user.
;
@@NoHelp:
mov bx, STDOUT ; everything to stdout
cmp [MsgLen], 0 ; anything to print out?
jz SHORT @@NoPrompt ; nope
mov dx, [MsgTxt]
call fputs
@@NoPrompt:
cmp [TFlag], 0 ; need to wait?
jz SHORT @@KeyIn ; no
mov ax, [Delay] ; yes, so...
call pause ; pause
jz SHORT @@KeyIn ; zf means a key is ready
ZERO al ; set return code to zero
jmp SHORT @@NewLine
@@KeyIn:
call getchar
;
; Convert character in AL as necessary. NB: if both '-l' and '-u' options
; are specified, the return value will be based on *uppercase* value.
;
cmp [LFlag], 0 ; convert to lowercase?
jz SHORT @@MaybeUpper ; no
call tolower
@@MaybeUpper:
cmp [UFlag], 0 ; convert to uppercase?
jz SHORT @@NewLine ; no
call toupper
;
; Add a final newline to keep things neat.
;
@@NewLine:
mov dx, OFFSET EOL + 1
call fputs
;
; Ok, let's terminate the program. Return code is already in AL.
;
@@Fin:
mov ah, 4ch
int DOS
EVEN
Buffer db ? ; space for single character
; nb: shared by fgetc() & fputc()
;-------------------------------------------------------------------------;
; Purpose: Reads a character from specified device.
; Notes: No checks are done on BX's validity.
; Buffer is shared by fputc(). Do *NOT* use in a
; multitasking environment like DESQview.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: BX = device handle.
; Exit: AL = character,
; Carry flag set on error (AX holds error code).
; Calls: none
; Changes: AX
; flags
;-------------------------------------------------------------------------;
PROC fgetc
push cx dx
IF @DataSize NE 0
push ds
mov ax, @data
mov ds, ax
ENDIF
mov dx, OFFSET Buffer ; point to storage
mov cx, 1 ; only need 1 char
mov ah, 3fh
int DOS ; get it
jc SHORT @@Fin ; abort on error
mov al, [Buffer]
@@Fin:
IF @DataSize NE 0
pop ds
ENDIF
pop dx cx
ret
ENDP fgetc
;-------------------------------------------------------------------------;
; Purpose: Writes a character to specified device.
; Notes: No checks are done on BX's validity.
; Buffer is shared by fputc(). Do *NOT* use in a
; multitasking environment like DESQview.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: AL = character to display,
; BX = device handle.
; Exit: AL = 1 if successful,
; Carry flag set on error (AX holds error code).
; Calls: none
; Changes: AX
;-------------------------------------------------------------------------;
PROC fputc
push cx dx
IF @DataSize NE 0
push ds
mov dx, @data
mov ds, ax
ENDIF
mov dx, OFFSET Buffer ; point to storage
mov [Buffer], al ; save char
mov cx, 1 ; only write 1 char
mov ah, 40h
int DOS
IF @DataSize NE 0
pop ds
ENDIF
pop dx cx
ret
ENDP fputc
;-------------------------------------------------------------------------;
; Purpose: Reads a character from STDIN.
; Notes: Character is echoed to display.
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: AL = character.
; Calls: none
; Changes: AX
;-------------------------------------------------------------------------;
PROC getchar
mov ah, 1
int DOS
ret
ENDP getchar
;-------------------------------------------------------------------------;
; Purpose: Writes a character to STDOUT device.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: DL = character to display.
; Exit: n/a
; Calls: none
; Changes: none
;-------------------------------------------------------------------------;
PROC putchar
push ax
mov ah, 2
int DOS
pop ax
ret
ENDP putchar
;-------------------------------------------------------------------------;
; Purpose: Checks if a character is ready for input from STDIN.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: zf = 1 if character available.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC kbhit
push ax
mov ah, 0bh
int DOS
cmp al, 0ffh ; AL = FFh if character ready
pop ax
ret
ENDP kbhit
EVEN
;-------------------------------------------------------------------------;
; Purpose: Writes an ASCIIZ string to specified device.
; Notes: A zero-length string doesn't seem to cause problems when
; this output function is used.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: BX = device handle,
; DS:DX = pointer to string.
; Exit: Carry flag set if EOS wasn't found or handle is invalid.
; Calls: strlen
; Changes: none
;-------------------------------------------------------------------------;
PROC fputs
push ax cx di es
mov ax, ds
mov es, ax
mov di, dx
call strlen ; set CX = length of string
jc SHORT @@Fin ; abort if problem finding end
mov ah, 40h ; MS-DOS raw output function
int DOS
@@Fin:
pop es di cx ax
ret
ENDP fputs
EVEN
;-------------------------------------------------------------------------;
; Purpose: Writes an error message to stderr.
; Notes: none
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: DS:DX = pointer to error message.
; Exit: n/a
; Calls: fputs
; Changes: none
;-------------------------------------------------------------------------;
PROC errmsg
push bx dx
mov bx, STDERR
mov dx, OFFSET ProgName ; display program name
call fputs
pop dx ; recover calling parameters
push dx ; and save again to avoid change
call fputs ; display error message
mov dx, OFFSET EOL
call fputs
pop dx bx
ret
ENDP errmsg
EVEN
;-------------------------------------------------------------------------;
; Purpose: Gets current system date, based on DOS's internal clock.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: AL = day of week (0 = Sunday)
; DL = day (1 to 31)
; DH = month (1 to 12)
; CX = year (1980 to 2099)
; Calls: none
; Changes: AX, CX, DX
;-------------------------------------------------------------------------;
PROC getdate
mov ah, 2ah ; MS-DOS get system date function
int DOS
ret
ENDP getdate
;-------------------------------------------------------------------------;
; Purpose: Gets current system time, based on DOS's internal clock.
; Notes: none
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: n/a
; Exit: CL = minutes (0 - 59)
; CH = hour (0 - 23)
; DL = hundredths of seconds (0 - 99)
; DH = seconds (0 - 59)
; Calls: none
; Changes: CX, DX
;-------------------------------------------------------------------------;
PROC gettime
push ax
mov ah, 2ch ; MS-DOS get system time function
int DOS
pop ax
ret
ENDP gettime
EVEN
;-------------------------------------------------------------------------;
; Purpose: Pauses execution until a specified time.
; Notes: If time is 12 or more hours in advance, execution aborts.
; Range for hours is 0-23, for minutes is 0-59, etc...
; This is as accurate as the DOS clock.
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: AX:BX = time of day (hh.mm.ss.hs) at which to awaken.
; Exit: n/a
; Calls: gettime
; Changes: flags
;-------------------------------------------------------------------------;
PROC at
push cx dx
@@TimeLoop:
call gettime ; get current time
sub cx, ax ; cx = current time - alarm time
jz SHORT @@CheckSecs ; if zero, check seconds
cmp ah, 12 ; adjust if alarm is a.m. ...
jae SHORT @@TooFar?
cmp ch, 0 ; and current time is p.m. ...
jl SHORT @@TooFar? ; (signed comparison!!!)
sub ch, 24 ; by subtracting 24 hours
@@TooFar?:
neg cx ; cx = alarm time - current time
cmp cx, 12 * 256 ; more than 12 hours difference?
ja SHORT @@Fin ; yep (unsigned comparison!!!)
jmp @@TimeLoop ; nope (already checked if ==)
@@CheckSecs:
cmp dx, bx ; wait a few more seconds?
jb @@TimeLoop ; yep
or dl, 1 ; no, clear zf
@@Fin:
pop dx cx
ret
ENDP at
;-------------------------------------------------------------------------;
; Purpose: Pauses execution for a specified number of seconds or
; until a keypress is detected.
; Notes: Delay should be less than 12 hours (43200 seconds) to
; avoid checks on date rollover yet ensure we haven't
; paused too long. Delay is not checked however!
; This procedure works by adding the delay to the current
; time and waiting until then. If the system clock is
; adjusted in meantime, results are unpredictable. I
; tried looping and calling gettime(), but that was
; inaccurate due to roundoff of hundreths of secs.
; Requires: 8086-class CPU and DOS v2.0 or better.
; Entry: AX = delay time (in seconds).
; Exit: Zero flag set if input ready; cleared otherwise.
; Calls: gettime, kbhit
; Changes: flags
;-------------------------------------------------------------------------;
PROC pause
push ax bx cx dx
ZERO bx
mov cx, 60 ; 60 secs/min and 60 mins/hour
ZERO dx
div cx ; now ax = hours and minutes
mov bh, dl ; and bh = seconds (dh == 0)
ZERO dx
div cx ; now ax = hours, dx = minutes
mov ah, al ; hours
mov al, dl ; minutes
;
; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
;
call gettime ; get current time
add ax, cx ; compute alarm time
add bx, dx
cmp bh, 60 ; too many seconds?
jb SHORT @@CheckMins ; nope
sub bh, 60 ; yep, adjust
inc al
@@CheckMins:
cmp al, 60 ; too many minutes?
jb SHORT @@CheckHours ; nope
sub al, 60 ; yep, adjust
inc ah
@@CheckHours:
cmp ah, 24 ; too many hours?
jb SHORT @@TimeLoop ; nope
sub ah, 24 ; yep, adjust
;
; Here's the main loop. Check for both keypress and alarm time.
; NB: Because of overhead in the code it's possible to overshoot
; the alarm time by a few hundreths of a second so check for that.
;
@@TimeLoop:
call kbhit ; check for user input
jz SHORT @@Fin ; and abort if present
call gettime ; get current time
sub cx, ax ; cx = current time - alarm time
jz SHORT @@CheckSecs ; if zero, check seconds
cmp ah, 12 ; adjust if alarm is a.m. ...
jae SHORT @@TooFar?
cmp ch, 0 ; and current time is p.m. ...
jl SHORT @@TooFar? ; (signed comparison!!!)
sub ch, 24 ; by subtracting 24 hours
@@TooFar?:
neg cx ; cx = alarm time - current time
cmp cx, 12 * 256 ; more than 12 hours difference?
ja SHORT @@Fin ; yep (unsigned comparison!!!)
jmp @@TimeLoop ; nope (already checked if ==)
@@CheckSecs:
cmp dx, bx ; wait a few more seconds?
jb @@TimeLoop ; yep
or dl, 1 ; no, clear zf
@@Fin:
pop dx cx bx ax
ret
ENDP pause
;-------------------------------------------------------------------------;
; Purpose: Pauses execution for a specified number of seconds.
; Notes: Delay should be less than 12 hours (43200 seconds) due
; to the way at() is implemented. This is not checked!
; This procedure works by adding the delay to the current
; time and waiting until then. If the system clock is
; adjusted in meantime, results are unpredictable. I
; tried looping and calling gettime(), but that was
; inaccurate due to roundoff of hundreths of secs.
; Requires: 8086-class CPU and DOS v1.0 or better.
; Entry: AX = delay time (in seconds).
; Exit: n/a
; Calls: gettime, at
; Changes: none
;-------------------------------------------------------------------------;
PROC sleep
push ax bx cx dx
ZERO bx
mov cx, 60 ; 60 secs/min and 60 mins/hour
ZERO dx
div cx ; now ax = hours and minutes
mov bh, dl ; and bh = seconds (dh == 0)
ZERO dx
div cx ; now ax = hours, dx = minutes
mov ah, al ; hours
mov al, dl ; minutes
;
; At this point, AX:BX = amount of time to sleep (hh.mm.ss.hs).
;
call gettime ; get current time
add ax, cx ; compute alarm time
add bx, dx
cmp bh, 60 ; too many seconds?
jb SHORT @@CheckMins ; nope
sub bh, 60 ; yep, adjust
inc al
@@CheckMins:
cmp al, 60 ; too many minutes?
jb SHORT @@CheckHours ; nope
sub al, 60 ; yep, adjust
inc ah
@@CheckHours:
cmp ah, 24 ; too many hours?
jb SHORT @@Fin ; nope
sub ah, 24 ; yep, adjust
@@Fin:
call at ; pause until alarm time
pop dx cx bx ax
ret
ENDP sleep
EVEN
;-------------------------------------------------------------------------;
; Purpose: Converts string of digits to an *unsigned* integer in
; range [0, 65535].
; Notes: Conversion stops with first non-numeric character.
; Requires: 8086-class CPU.
; Entry: DS:SI = pointer to string of digits.
; Exit: AX = unsigned integer (garbage if cf = 1),
; DS:SI = pointer to first non-digit found,
; cf = 1 if number is too big.
; Calls: none
; Changes: AX, SI
; flags
;-------------------------------------------------------------------------;
PROC atou
push bx cx dx ; DX destroyed by MUL below
ZERO ax ; AX = digit to convert
ZERO bx ; BX = integer word
mov cx, 10 ; CX = conversion factor
@@NextCh:
mov bl, [si] ; get character
cmp bl, '0' ; test if a digit
jb SHORT @@Fin
cmp bl, '9'
ja SHORT @@Fin
inc si ; bump up pointer
mul cx ; multiply old result by 10
jc SHORT @@Overflow
sub bl, '0' ; convert digit
add ax, bx ; add current value
jnc @@NextCh ; continue unless result too big
@@Overflow:
ZERO cx ; denotes overflow
jmp @@NextCh
@@Fin:
cmp cx, 10 ; cf = (cx != 10)
pop dx cx bx
ret
ENDP atou
EVEN
;-------------------------------------------------------------------------;
; Purpose: Tests if character is a valid ASCII digit.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC isdigit
cmp al, '0' ; if < '0' zf = 0
jb SHORT @@Fin
cmp al, '9' ; if > '9' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
ret
ENDP isdigit
;-------------------------------------------------------------------------;
; Purpose: Tests if character is lowercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC islower
cmp al, 'a' ; if < 'a' zf = 0
jb SHORT @@Fin
cmp al, 'z' ; if > 'z' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
ret
ENDP islower
;-------------------------------------------------------------------------;
; Purpose: Tests if character is uppercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC isupper
cmp al, 'A' ; if < 'A' zf = 0
jb SHORT @@Fin
cmp al, 'Z' ; if > 'Z' zf = 0
ja SHORT @@Fin
cmp al, al ; set Z flag
@@Fin:
ret
ENDP isupper
;-------------------------------------------------------------------------;
; Purpose: Tests if character is an ASCII whitespace.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be tested.
; Exit: Zero flag set if true, cleared otherwise.
; Calls: none
; Changes: flags
;-------------------------------------------------------------------------;
PROC iswhite
cmp al, SPACE ; if == SPACE then zf = 1
jz SHORT @@Fin
cmp al, TAB ; if == TAB then zf = 1
jz SHORT @@Fin
cmp al, LF ; if == LF then zf = 1
jz SHORT @@Fin
cmp al, CR ; if == CR then zf = 1
@@Fin:
ret
ENDP iswhite
EVEN
;-------------------------------------------------------------------------;
; Purpose: Converts character to lowercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be converted.
; Exit: AL = converted character.
; Calls: none
; Changes: AL
; flags
;-------------------------------------------------------------------------;
PROC tolower
cmp al, 'A' ; if < 'A' then done
jb SHORT @@Fin
cmp al, 'Z' ; if > 'Z' then done
ja SHORT @@Fin
or al, 20h ; make it lowercase
@@Fin:
ret
ENDP tolower
;-------------------------------------------------------------------------;
; Purpose: Converts character to uppercase.
; Notes: none
; Requires: 8086-class CPU.
; Entry: AL = character to be converted.
; Exit: AL = converted character.
; Calls: none
; Changes: AL
; flags
;-------------------------------------------------------------------------;
PROC toupper
cmp al, 'a' ; if < 'a' then done
jb SHORT @@Fin
cmp al, 'z' ; if > 'z' then done
ja SHORT @@Fin
and al, not 20h ; make it lowercase
@@Fin:
ret
ENDP toupper
EVEN
;-------------------------------------------------------------------------;
; Purpose: Calculates length of an ASCIIZ string.
; Notes: Terminal char is _not_ included in the count.
; Requires: 8086-class CPU.
; Entry: ES:DI = pointer to string.
; Exit: CX = length of string,
; cf = 0 and zf = 1 if EOS found,
; cf = 1 and zf = 0 if EOS not found within segment.
; Calls: none
; Changes: CX,
; flags
;-------------------------------------------------------------------------;
PROC strlen
push ax di
pushf
cld ; scan forward only
mov al, EOS ; character to search for
mov cx, di ; where are we now
not cx ; what's left in segment - 1
push cx ; save char count
repne scasb
je SHORT @@Done
scasb ; test final char
dec cx ; avoids trouble with "not" below
@@Done:
pop ax ; get original count
sub cx, ax ; subtract current count
not cx ; and invert it
popf ; restore df
dec di
cmp [BYTE PTR es:di], EOS
je SHORT @@Fin ; cf = 0 if equal
stc ; set cf => error
@@Fin:
pop di ax
ret
ENDP strlen
END